home *** CD-ROM | disk | FTP | other *** search
- /* (C) Copyright 1993,1994 by Carnegie Mellon University
- * All Rights Reserved.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without
- * fee, provided that the above copyright notice appear in all copies
- * and that both that copyright notice and this permission notice
- * appear in supporting documentation, and that the name of Carnegie
- * Mellon University not be used in advertising or publicity
- * pertaining to distribution of the software without specific,
- * written prior permission. Carnegie Mellon University makes no
- * representations about the suitability of this software for any
- * purpose. It is provided "as is" without express or implied
- * warranty.
- *
- * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
- * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
- * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <string.h>
- #include "xmalloc.h"
- #include "common.h"
- #include "part.h"
-
- extern char *os_idtodir();
- extern FILE *os_newtypedfile();
-
- static FILE *startDescFile();
-
-
- /* Length of a normal uuencoded line, including newline */
- #define UULENGTH 62
-
- /*
- * Table of valid boundary characters
- *
- * XXX: Old versions of Mark Crispin's c-client library
- * generate boundaries which contain the syntactically
- * illegal character '#'. It is marked in this table with
- * a 2 in case we want to use this table in the future to
- * complain about bad syntax.
- *
- */
- static char bchar[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- };
-
- /*
- * Read an input file, looking for data in split-uuencode format
- */
- handleUuencode(inpart, subject, extractText)
- struct part *inpart;
- char *subject;
- int extractText;
- {
- char *fname = 0, *tmpfname;
- int part, nparts;
- int tmppart, tmpnparts;
- char buf[1024], buf2[1024];
- char fnamebuf[80];
- char *boundary_end, *p;
- int wantdescfile = 0;
- FILE *descfile = 0;
-
- /* Scan "Subject:" header for filename/part information */
- if (parseSubject(subject, &fname, &part, &nparts) != 0) {
- part = -1;
- }
- if (part == 0) {
- return saveUuFile(inpart, fname, part, nparts, (char *)0);
- }
- if (part == 1) {
- wantdescfile = 1;
- }
-
- /* Scan body for interesting lines */
- while (part_gets(buf, sizeof(buf), inpart)) {
- /* Uuencode "begin" line */
- if (!strncmp(buf, "begin ", 6) &&
- isdigit(buf[6]) && isdigit(buf[7]) && isdigit(buf[8]) &&
- buf[9] == ' ') {
- if (part == -1) {
- /*
- * We have no part N of M information. Perhaps it is
- * a single-part uuencoded file.
- */
- return saveUuFile(inpart, (char *)0, 1, 0, buf);
- }
- else {
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, fname, part, nparts, buf);
- }
- }
- else if (!strncmp(buf, "section ", 8) && isdigit(buf[8])) {
- tmppart = 0;
- for (p = buf+8; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
- if (tmppart == 0) continue;
- if (strncmp(p, " of ", 4) == 0) {
- /*
- * "section N of ... of file F ..."
- */
- for (p += 4; *p && strncmp(p, " of file ", 9) != 0; p++);
- if (!*p) continue;
- p += 9;
- tmpfname = p;
- p = strchr(p, ' ');
- if (!p) continue;
- *p = '\0';
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, 0, (char *)0);
- }
- else if (*p == '/' && isdigit(p[1])) {
- /*
- * "section N/M file F ..."
- */
- tmpnparts = 0;
- for (p++; isdigit(*p); p++) {
- tmpnparts = tmpnparts*10 + *p - '0';
- }
- while (*p && isspace(*p)) p++;
- if (tmppart > tmpnparts || strncmp(p, "file ", 5) != 0) {
- continue;
- }
- tmpfname = p+5;
- p = strchr(tmpfname, ' ');
- if (!p) continue;
- *p = '\0';
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, tmpnparts,
- (char *)0);
- }
- }
- else if (!strncmp(buf, "POST V", 6)) {
- /*
- * "POST Vd.d.d F (Part N/M)"
- */
- p = strchr(buf+6, ' ');
- if (!p) continue;
- tmpfname = p+1;
- p = strchr(tmpfname, ' ');
- if (!p || strncmp(p, " (Part ", 7) != 0) continue;
- *p = '\0';
- p += 7;
- tmppart = 0;
- while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
- if (tmppart == 0 || *p++ != '/') continue;
- tmpnparts = 0;
- while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
- if (tmppart > tmpnparts || *p != ')') continue;
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
- }
- else if (!strncmp(buf, "File: ", 6)) {
- /*
- * "File: F -- part N of M -- ...
- */
- tmpfname = buf+6;
- p = strchr(tmpfname, ' ');
- if (!p || strncmp(p, " -- part ", 9) != 0) continue;
- *p = '\0';
- p += 9;
- tmppart = 0;
- while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
- if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
- p += 4;
- tmpnparts = 0;
- while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
- if (tmppart > tmpnparts || strncmp(p, " -- ", 4) != 0) continue;
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
- }
- else if (!strncmp(buf, "[Section: ", 10)) {
- /*
- * "[Section: N/M File: F ..."
- */
- tmppart = 0;
- for (p = buf+10; isdigit(*p); p++) tmppart = tmppart*10 + *p - '0';
- if (tmppart == 0) continue;
- tmpnparts = 0;
- for (p++; isdigit(*p); p++) {
- tmpnparts = tmpnparts*10 + *p - '0';
- }
- while (*p && isspace(*p)) p++;
- if (tmppart > tmpnparts || strncmp(p, "File: ", 6) != 0) {
- continue;
- }
- tmpfname = p+6;
- p = strchr(tmpfname, ' ');
- if (!p) continue;
- *p = '\0';
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
- }
- else if (*buf == '[') {
- /*
- * "[F ... - part N of M]"
- * (usual BinHex practice)
- */
- tmpfname = buf+1;
- p = strchr(tmpfname, ' ');
- if (!p) continue;
- *p++ = '\0';
- while (p && strncmp(p, "- part ", 7) != 0) {
- p = strchr(p+1, '-');
- }
- if (!p) continue;
- p += 7;
- tmppart = 0;
- while (isdigit(*p)) tmppart = tmppart*10 + *p++ - '0';
- if (tmppart == 0 || strncmp(p, " of ", 4) != 0) continue;
- p += 4;
- tmpnparts = 0;
- while (isdigit(*p)) tmpnparts = tmpnparts*10 + *p++ - '0';
- if (tmppart > tmpnparts || *p != ']') continue;
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, tmpfname, tmppart, tmpnparts, (char *)0);
- }
- else if (fname && part > 0 && nparts > 0 && part <= nparts &&
- (!strncmp(buf, "BEGIN", 5) ||
- !strncmp(buf, "--- BEGIN ---", 12) ||
- (buf[0] == 'M' && strlen(buf) == UULENGTH))) {
- /*
- * Found the start of a section of uuencoded data
- * and have the part N of M information.
- */
- if (descfile) fclose(descfile);
- return saveUuFile(inpart, fname, part, nparts, buf);
- }
- else if (!cistrncmp(buf, "x-file-name: ", 13)) {
- for (p = buf + 13; *p && !isspace(*p); p++);
- *p = '\0';
- strncpy(fnamebuf, buf+13, sizeof(fnamebuf)-1);
- fnamebuf[sizeof(fnamebuf)-1] = '\0';
- fname = fnamebuf;
- continue;
- }
- else if (!cistrncmp(buf, "x-part: ", 8)) {
- tmppart = atoi(buf+8);
- if (tmppart > 0) part = tmppart;
- continue;
- }
- else if (!cistrncmp(buf, "x-part-total: ", 14)) {
- tmpnparts = atoi(buf+14);
- if (tmpnparts > 0) nparts = tmpnparts;
- continue;
- }
- else if (part == 1 && fname && !descfile &&
- !cistrncmp(buf, "x-file-desc: ", 13)) {
- if (descfile = startDescFile(fname)) {
- fputs(buf+13, descfile);
- fclose(descfile);
- descfile = 0;
- }
- continue;
- }
- else if (!strcmp(buf,
- "(This file must be converted with BinHex 4.0)\n")) {
- if (descfile) fclose(descfile);
- return os_binhex(inpart, 1, 1);
- }
- else if (!cistrncmp(buf, "content-", 8)) {
- /*
- * HEURISTIC: If we see something that looks like a content-*
- * header, push it back and call the message parser.
- */
- p = buf+8;
- /* Check to see if header's field-name is syntactically valid */
- while (*p) {
- if (*p == ':' || *p <= ' ' || *p >= '\177') break;
- p++;
- }
- if (*p == ':') {
- part_ungets(buf, inpart);
- if (descfile) fclose(descfile);
- return handleMessage(inpart, "text/plain", 0, extractText);
- }
- }
- if (buf[0] == '-' && buf[1] == '-') {
- /*
- * Heuristic: If we see something that looks like a
- * multipart boundary, followed by something that looks
- * like a header, push them back and parse as a multipart.
- */
- p = buf+2;
- while (*p) {
- if (!bchar[(unsigned char)*p]) break;
- p++;
- }
- if (*p != '\n') {
- /*
- * We found an invalid boundary character.
- * Move 'p' such that it will fail all subsequent checks.
- */
- p = buf + 2;
- }
- /* Back up to ignore trailing whitespace */
- while (p > buf+2 && p[-1] == ' ') p--;
-
- /*
- * Check that boundary is within legal size limits
- * If so, peek at next line
- */
- if (p - buf > 2 && p - buf <= 72 &&
- part_gets(buf2, sizeof(buf2), inpart)) {
- boundary_end = p;
- p = buf2;
- /*
- * Check to see if a syntactically valid header follows
- * what looks to be a boundary.
- *
- * XXX: Unfortunately, we can't check for "Content-";
- * it is syntactically valid to have a body-part
- * header that doesn't start with that and ZMail
- * takes advantage of that. If this heuristic starts
- * causing problems, we could keep looking ahead until
- * we find a "Content-" header or find something that's
- * not a header.
- */
- while (*p) {
- if (*p == ':' || *p <= ' ' || *p >= '\177') break;
- p++;
- }
-
- /* Push back the lookahead line */
- part_ungets(buf2, inpart);
-
- if (p > buf2 && *p == ':') {
- /* Push back the boundary */
- part_ungets(buf, inpart);
-
- /*
- * Generate and push back a header to get us into
- * the multipart parser.
- */
- *boundary_end = '\0';
- sprintf(buf2,
- "Content-type: multipart/mixed; boundary=\"%s\"\n\n",
- buf+2);
- part_ungets(buf2, inpart);
-
- if (descfile) fclose(descfile);
- return handleMessage(inpart, "text/plain", 0, extractText);
- }
- }
- }
-
- /*
- * Save useful-looking text that is before a "part 1 of N"
- * in a description file.
- */
- if (wantdescfile && !descfile) {
- for (p = buf; *p && isspace(*p); p++);
- if (*p) {
- if (!cistrncmp(p, "x-", 2)) {
- /*
- * Check for "X-foobar:"
- * If so, there probably will be a "X-File-Desc:" line
- * later, so ignore this line.
- */
- while (*p != ':' && *p > ' ' && *p < '\177') p++;
- if (*p == ':') continue;
- }
- if (!descEnd(buf) && (descfile = startDescFile(fname))) {
- fputs(buf, descfile);
- }
- wantdescfile = 0;
- }
- }
- else if (descfile) {
- if (descEnd(buf)) {
- fclose(descfile);
- descfile = 0;
- }
- else {
- fputs(buf, descfile);
- }
- }
- }
-
- if (descfile) fclose(descfile);
- return 0;
- }
-
- /*
- * Handle a split-uuencode part
- * If nparts is 0, then look for an "end" line to detect the last part.
- * If fname is null, then we are attempting to decode a single-part message.
- * If firstline is non-null, it is written as the first line of the saved part
- */
- int
- saveUuFile(inpart, fname, part, nparts, firstline)
- struct part *inpart;
- char *fname;
- int part;
- int nparts;
- char *firstline;
- {
- char buf[1024];
- char *dir;
- FILE *partfile;
-
- if (fname) {
- sprintf(buf, "Saving part %d ", part);
- if (nparts) sprintf(buf+strlen(buf), "of %d ", nparts);
- strcat(buf, fname);
- chat(buf);
- }
- else fname = "unknown";
-
- /* Create directory to store parts and copy this part there. */
- dir = os_idtodir(fname);
- if (!dir) return 1;
- sprintf(buf, "%s%d", dir, part);
- partfile = fopen(buf, "w");
- if (!partfile) {
- os_perror(buf);
- return 1;
- }
- if (firstline) fputs(firstline, partfile);
- while (part_gets(buf, sizeof(buf), inpart)) {
- fputs(buf, partfile);
- if (nparts == 0 && strcmp(buf, "end\n") == 0) {
- /* This is the last part. Remember the fact */
- nparts = part;
- fclose(partfile);
- sprintf(buf, "%sCT", dir);
- partfile = fopen(buf, "w");
- if (!partfile) {
- os_perror(buf);
- }
- else {
- fprintf(partfile, "%d\n", nparts);
- }
- break;
- }
- }
- fclose(partfile);
-
- /* Retrieve any previously saved number of the last part */
- if (nparts == 0) {
- sprintf(buf, "%sCT", dir);
- if (partfile = fopen(buf, "r")) {
- if (fgets(buf, sizeof(buf), partfile)) {
- nparts = atoi(buf);
- if (nparts < 0) nparts = 0;
- }
- fclose(partfile);
- }
- }
-
- if (nparts == 0) return 0;
-
- /* Check to see if we have all parts. Start from the highest numbers
- * as we are more likely not to have them.
- */
- for (part = nparts; part; part--) {
- sprintf(buf, "%s%d", dir, part);
- partfile = fopen(buf, "r");
- if (partfile) {
- fclose(partfile);
- }
- else {
- return 0;
- }
- }
-
- return uudecodefiles(dir, nparts);
- }
-
- /*
- * Parse a Subject: header, looking for clues with which to decode
- * split-uuencoded data.
- */
- int
- parseSubject(subject, fnamep, partp, npartsp)
- char *subject;
- char **fnamep;
- int *partp;
- int *npartsp;
- {
- char *scan, *bak, *start;
- int part = -1, nparts = 0, hasdot = 0;
-
- /* No subject header */
- if (!subject) return 1;
-
- /* Skip leading whitespace and other garbage */
- scan = subject;
- while (*scan == ' ' || *scan == '\t' || *scan == '-') scan++;
- if (!cistrncmp(scan, "repost", 6)) {
- for (scan += 6; *scan == ' ' || *scan == '\t'
- || *scan == ':' || *scan == '-'; scan++);
- }
-
- /* Replies aren't usually data */
- if (!cistrncmp(scan, "re:", 3)) return 1;
-
- /* Get filename */
-
- /* Grab the first filename-like string. Explicitly ignore strings with
- * prefix "v<digit>" ending in ":", since that is a popular volume/issue
- * representation syntax
- */
- do {
- while (*scan != '\n' && isprint(*scan)
- && !isalnum(*scan) && *scan != '_') ++scan;
- *fnamep = start = scan;
- while (isalnum(*scan) || *scan == '-' || *scan == '+' || *scan == '&'
- || *scan == '_' || *scan == '.') {
- if (*scan++ == '.') hasdot = 1;
- }
- if (!*scan || *scan == '\n') return 1;
- } while (start == scan
- || (start[0] == 'v' && isdigit(start[1]) && *scan == ':'));
- *scan++ = '\0';
-
- /* Try looking for a filename with a "." in it later in the subject line.
- * Exclude <digit>.<digit>, since that is usually a version number.
- */
- if (!hasdot) {
- while (*(start = scan) != '\0' && *scan != '\n') {
- while (isspace(*start)) ++start;
- for (scan = start; isalnum(*scan) || *scan == '-' || *scan == '+'
- || *scan == '&' || *scan == '_' || *scan == '.'; ++scan) {
- if (*scan == '.' &&
- (!isdigit(scan[-1]) || !isdigit(scan[1]))) {
- hasdot = 1;
- }
- }
- if (hasdot && scan > start) {
- *fnamep = start;
- *scan++ = '\0';
- break;
- }
- while (*scan && *scan != '\n' && !isalnum(*scan)) ++scan;
- }
- scan = *fnamep + strlen(*fnamep) + 1;
- }
-
- /* Get part number */
- while (*scan && *scan != '\n') {
- /* skip over versioning */
- if (*scan == 'v' && isdigit(scan[1])) {
- ++scan;
- while (isdigit(*scan)) ++scan;
- }
- /* look for "1/6" or "1 / 6" or "1 of 6" or "1-of-6" or "1o6" */
- if (isdigit(*scan) &&
- (scan[1] == '/'
- || (scan[1] == ' ' && scan[2] == '/')
- || (scan[1] == ' ' && scan[2] == 'o' && scan[3] == 'f')
- || (scan[1] == '-' && scan[2] == 'o' && scan[3] == 'f')
- || (scan[1] == 'o' && isdigit(scan[2])))) {
- while (isdigit(scan[-1])) scan--;
- part = 0;
- while (isdigit(*scan)) {
- part = part * 10 + *scan++ - '0';
- }
- while (*scan != '\0' && *scan != '\n' && !isdigit(*scan)) scan++;
- if (isdigit(*scan)) {
- nparts = 0;
- while (isdigit(*scan)) {
- nparts = nparts * 10 + *scan++ - '0';
- }
- }
- break;
- }
-
- /* look for "6 parts" or "part 1" */
- if (!cistrncmp("part", scan, 4)) {
- if (scan[4] == 's') {
- for (bak = scan; bak >= subject && !isdigit(*bak); bak--);
- if (bak > subject) {
- while (bak > subject && isdigit(bak[-1])) bak--;
- nparts = 0;
- while (isdigit(*bak)) {
- nparts = nparts * 10 + *bak++ - '0';
- }
- }
- } else {
- while (*scan && *scan != '\n' && !isdigit(*scan)) scan++;
- bak = scan - 1;
- if (isdigit(*scan)) {
- part = 0;
- do {
- part = part * 10 + *scan++ - '0';
- } while (isdigit(*scan));
- }
- scan = bak;
- }
- }
- scan++;
- }
-
- if (nparts == 0 || part == -1 || part > nparts) return 1;
- *partp = part;
- *npartsp = nparts;
- return 0;
- }
-
- /*
- * Return nonzero if 'line' should mark the end of a part-1 description
- */
- int
- descEnd(line)
- char *line;
- {
- return !strncmp(line, "---", 3) ||
- !strncmp(line, "#!", 2) ||
- !cistrncmp(line, "part=", 5) ||
- !cistrncmp(line, "begin", 5);
- }
-
- /*
- * Open and return a file pointer for a description file for 'fname'.
- * If a description file for 'fname' already exists, or if there is an
- * error, return a null pointer.
- */
- static FILE *startDescFile(fname)
- char *fname;
- {
- char buf[1024];
- char *dir;
- FILE *descfile;
-
- /* Create directory to store parts and copy this part there. */
- dir = os_idtodir(fname);
- if (!dir) return 0;
- sprintf(buf, "%s0", dir);
-
- /* See if part 0 already exists, return failure if so */
- descfile = fopen(buf, "r");
- if (descfile) {
- fclose(descfile);
- return 0;
- }
-
- descfile = fopen(buf, "w");
- if (!descfile) {
- os_perror(buf);
- return 0;
- }
- return descfile;
- }
-
- /*
- * Decode the uuencoded file that is in 'nparts' pieces in 'dir'.
- */
- int
- uudecodefiles(dir, nparts)
- char *dir;
- int nparts;
- {
- int part;
- enum {st_start, st_inactive, st_decode, st_nextlast, st_last,
- st_binhex} state;
- FILE *infile;
- FILE *outfile = NULL;
- struct part *inpart;
- char buf[1024];
- char lastline[UULENGTH+1];
- char *fname, *p;
- char *contentType = "application/octet-stream";
- int line_length = 0;
-
- /* If a part 0, copy to description filename */
- sprintf(buf, "%s0", dir);
- infile = fopen(buf, "r");
- if (infile) {
- outfile = fopen(TEMPFILENAME, "w");
- if (outfile) {
- while (fgets(buf, sizeof(buf), infile)) {
- fputs(buf, outfile);
- }
- fclose(outfile);
- outfile = NULL;
- }
- fclose(infile);
- sprintf(buf, "%s0", dir);
- remove(buf);
- }
-
- state = st_start;
-
- /* Handle each part in order */
- for (part = 1; part <= nparts; part++) {
- sprintf(buf, "%s%d", dir, part);
- infile = fopen(buf, "r");
- if (!infile) {
- os_perror(buf);
- if (outfile) fclose(outfile);
- remove(TEMPFILENAME);
- return 1;
- }
-
- while (fgets(buf, sizeof(buf), infile)) {
- switch (state) {
- case st_start: /* Looking for start of uuencoded
- * or binhex'ed file */
- if (!strcmp(buf,
- "(This file must be converted with BinHex 4.0)\n")) {
- state = st_binhex;
- inpart = part_init(infile);
- os_binhex(inpart, part, nparts);
- part_close(inpart);
- goto endbinhex;
- }
- if (strncmp(buf, "begin ", 6)) break;
- /* skip mode */
- p = buf + 6;
- while (*p && !isspace(*p)) p++;
- while (*p && isspace(*p)) p++;
- fname = p;
- while (*p && !isspace(*p)) p++;
- *p = '\0';
- if (!*fname) return 1;
-
- /* Guess the content-type of common filename extensions */
- if (p = strrchr(fname, '.')) {
- if (!cistrcmp(p, ".gif")) contentType = "image/gif";
- if (!cistrcmp(p, ".jpg")) contentType = "image/jpeg";
- if (!cistrcmp(p, ".jpeg")) contentType = "image/jpeg";
- if (!cistrcmp(p, ".mpg")) contentType = "video/mpeg";
- if (!cistrcmp(p, ".mpeg")) contentType = "video/mpeg";
- }
-
- /* Create output file and start decoding */
- outfile = os_newtypedfile(fname, contentType, FILE_BINARY,
- (params) 0);
- if (!outfile) {
- fclose(infile);
- return 1;
- }
- state = st_decode;
- break;
-
- case st_inactive: /* Looking for uuencoded data to resume */
- if (*buf != 'M' || strlen(buf) != line_length) {
- if (*buf == 'B' && !strncmp(buf, "BEGIN", 5)) {
- state = st_decode;
- }
- break;
- }
- state = st_decode;
- /* FALL THROUGH */
- case st_decode: /* Decoding data */
- if (line_length == 0) line_length = strlen(buf);
- if (*buf == 'M' && strlen(buf) == line_length) {
- uudecodeline(buf, outfile);
- break;
- }
- if (strlen(buf) > line_length) {
- state = st_inactive;
- break;
- }
- /*
- * May be on nearing end of file.
- * Save this line in case we are.
- */
- strcpy(lastline, buf);
- if (*buf == ' ' || *buf == '`') {
- state = st_last;
- }
- else {
- state = st_nextlast;
- }
- break;
-
- case st_nextlast: /* May be nearing end of file */
- if (*buf == ' ' || *buf == '`') {
- state = st_last;
- }
- else {
- state = st_inactive;
- }
- break;
-
- case st_last: /* Should be at end of file */
- if (!strncmp(buf, "end", 3) && isspace(buf[3])) {
- /* Handle that last line we saved */
- uudecodeline(lastline, outfile);
- fclose(infile);
- os_closetypedfile(outfile);
- for (;part <= nparts; part++) {
- sprintf(buf, "%s%d", dir, part);
- remove(buf);
- }
- sprintf(buf, "%sCT", dir);
- remove(buf);
- os_donewithdir(dir);
- return 0;
- }
- state = st_inactive;
- break;
-
- case st_binhex:
- if (strncmp(buf, "---", 3)) break;
- inpart = part_init(infile);
- os_binhex(inpart, part, nparts);
- part_close(inpart);
- goto endbinhex;
- }
- }
- if (state != st_binhex) state = st_inactive;
- fclose(infile);
- endbinhex:
- sprintf(buf, "%s%d", dir, part);
- remove(buf);
- }
- if (outfile) os_closetypedfile(outfile);
- if (state == st_binhex) os_binhex(0, 0, 0);
- sprintf(buf, "%sCT", dir);
- remove(buf);
- os_donewithdir(dir);
- return 0;
- }
-
- #define DEC(c) (((c) - ' ') & 077)
-
- /*
- * Decode a uuencoded line to 'outfile'
- */
- uudecodeline(line, outfile)
- char *line;
- FILE *outfile;
- {
- int c, len;
-
- len = DEC(*line++);
- while (len) {
- c = DEC(*line) << 2 | DEC(line[1]) >> 4;
- putc(c, outfile);
- if (--len) {
- c = DEC(line[1]) << 4 | DEC(line[2]) >> 2;
- putc(c, outfile);
- if (--len) {
- c = DEC(line[2]) << 6 | DEC(line[3]);
- putc(c, outfile);
- len--;
- }
- }
- line += 4;
- }
- return;
- }
-
-
-